package com.me.common.util;

import com.me.study.javaCore.dynamicProxy.TargetImpl;
import com.me.study.javaCore.dynamicProxy.jdkDynamic.JdkDynamicAdvice;
import org.apache.commons.lang3.StringUtils;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理: 拿到代理对象的工具类
 *
 * @author ME
 * @date   2019/12/2
 */
public class JdkDynamicProxyUtil {

    /**
     * <pre>创建代理对象</pre>
     *
     * T 代表调用方的返回值类型，证明：<p>
     *     调用方返回值改为 JdkDynamicTarget 就会出现 ClassCastException($Proxy2 -> JdkDynamicTarget)
     */
    @SuppressWarnings("unchecked")
    public static <T> T getProxyObject(Object target, InvocationHandler jdkAdvice) throws ClassCastException {
        /*
         * param1: 类加载器--目标类所使用的类加载器<p>
         * param2: 代理实例所需实现的一组接口<p>
         * param3: 整合了 切面增强逻辑和 业务逻辑的 完整逻辑的调用处理器对象
         */

        // try {
        //     // Proxy.newProxyInstance() 最终调用为如下步骤：
        //     // 1、生成代理接口的Class（） class com.sun.proxy.$Proxy0
        //     // 2、拿到构造器：public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
        //     // 3、new一个InvocationHandler实例
        //     // 4、通过构造函数 new出一个实例
        //     Class<?> proxyClass = Proxy.getProxyClass(JdkDynamicProxyUtil.class.getClassLoader(), target.getClass().getInterfaces());
        //     Constructor<?> cons = proxyClass.getConstructor(InvocationHandler.class);
        //     InvocationHandler ih = new JdkDynamicAdvice(new TargetImpl());
        //     T t1 = (T) cons.newInstance(ih);
        // } catch (Exception e) {
        //     e.printStackTrace();
        // }

        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), jdkAdvice);
    }

    /**
     * 根据传入的接口类型, 查找 jvm生成的 proxy,并把字节码信息写入文件
     *
     * @param className 保存的类名
     * @param interfaceClass     需要保存的 class对象
     */
    public static void createProxyClassFile(String className, Class<?>... interfaceClass) {
        byte[] data = ProxyGenerator.generateProxyClass(className, interfaceClass);
        try {
            FileOutputStream out = new FileOutputStream(className + ".class");
            out.write(data);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}
